package com.fudan.es.search;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fudan.biz.data.entity.BigDataSource;
import com.fudan.biz.data.service.BigDataSourceService;
import com.fudan.biz.data.service.FieldMetaService;
import com.fudan.es.BuildQueryException;
import com.fudan.es.EsClient;
import com.fudan.es.entity.FieldMeta;
import com.fudan.es.entity.QueryPack;
import com.fudan.framework.utils.JsonUtils;
import com.fudan.framework.utils.MapUtils;

@Service
public class QueryService {

	private static final String OP_KEY = "op";
	private static final String FIELD_KEY = "field";
	private static final String VALUE_KEY = "value";
	private ObjectMapper objectMapper = new ObjectMapper();
	@Autowired
	private FieldMetaService fieldMetaService;
	@Autowired
	private EsClient esClient;
	@Autowired
	private BigDataSourceService dataSourceService;

	public List<QueryPack> buildQueryPacks(String querys) throws Exception {
		if(querys == null) {
			return new ArrayList<>();
		}
		List<QueryPack> list = objectMapper.readValue(querys, new TypeReference<List<QueryPack>>() {
		});
		return list;
	}

	public QueryBuilder buildQuery(List<QueryPack> packs) {
		BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
		for (QueryPack queryPack : packs) {
			FieldMeta fieldMeta = fieldMetaService.get(queryPack.getField());
			queryPack.setField(fieldMeta.getName());
			if(fieldMeta.getInNested()) {
				boolQuery.must(QueryBuilders.nestedQuery(fieldMeta.getNestedPath(), queryPack.query(), ScoreMode.None));
			}else {
				boolQuery.must(queryPack.query());
			}
		}
		return boolQuery;
	}

	public SearchResponseEntity search(String query) throws BuildQueryException {
		try {
			Map<String,Object> queryMap = JsonUtils.toObject(query, new TypeReference<Map<String,Object>>() {
			});
			String dsId = MapUtils.getMapValue(queryMap, "datasource");
			BigDataSource dataSource = dataSourceService.get(dsId);
			RestHighLevelClient client = esClient.getClient(dataSource.getDwId());
			QueryBuilder queryBuilder = buildQuery(MapUtils.getMapValue(queryMap, "datasource"),queryMap);
			SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().query(queryBuilder).from(0).size(15).trackTotalHits(true);
			SearchRequest searchRequest = new SearchRequest(dataSource.getPath()).source(sourceBuilder);
			SearchResponse searchResponse;
			searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
			return SearchResponseEntity.build(searchResponse);
		} catch (IOException e) {
			e.printStackTrace();
			throw BuildQueryException.of("查询出错",e.getMessage());
		}
	}

	public QueryBuilder buildQuery(String dsId,Map<String,Object> queryMap) throws BuildQueryException{
		BoolQueryBuilder rootQuery = QueryBuilders.boolQuery();
		rootQuery.must(QueryBuilders.matchAllQuery());
		if(queryMap == null) {
			return rootQuery;
		}
		List<Map<String,Object>> mustMapList = MapUtils.getMapValue(queryMap, "must");
		if(mustMapList != null) {
			BoolQueryBuilder mustQuery = QueryBuilders.boolQuery();
			for (Map<String, Object> mustMap : mustMapList) {
				String field = (String) mustMap.get(FIELD_KEY);
				if(field == null) {
					throw BuildQueryException.of("字段没有识别",mustMap);
				}
				FieldMeta fieldMeta = fieldMetaService.findFieldByName(dsId, field);
				if(fieldMeta == null) {
					throw BuildQueryException.of("没有找到字段信息",mustMap);
				}
				String value = (String) mustMap.get(VALUE_KEY);
				if(value != null) {
					value = value.trim();
				}else {
					throw BuildQueryException.of("值设置为null",mustMap);
				}
				String op = (String) mustMap.get(OP_KEY);
				switch(op) {
				case "=": {
					if(fieldMeta.getNestedPath()!=null) {
						mustQuery.must(QueryBuilders.nestedQuery(fieldMeta.getNestedPath(), QueryBuilders.termQuery(field, value), ScoreMode.Avg));
					}else {
						mustQuery.must(QueryBuilders.termQuery(field, value));// field = value
					}
					break;
				}
				case ":": {
					if(value.toString().matches("^\".+\"$")) {
						// field : "value"
						if(fieldMeta.getNestedPath()!=null) {
							mustQuery.must(QueryBuilders.nestedQuery(fieldMeta.getNestedPath(),QueryBuilders.matchPhraseQuery(field, value.substring(1, value.length()-1)), ScoreMode.Avg));// field : "value"
						}else {
							mustQuery.must(QueryBuilders.matchPhraseQuery(field, value.substring(1, value.length()-1)));
						}
					}else {
						// field : value
						if(fieldMeta.getNestedPath()!=null) {
							mustQuery.must(QueryBuilders.nestedQuery(fieldMeta.getNestedPath(),mustQuery.must(QueryBuilders.matchPhraseQuery(field, value)), ScoreMode.Avg));// field : "value"
						}else {
							mustQuery.must(mustQuery.must(QueryBuilders.matchPhraseQuery(field, value)));
						}

					}
					break;
				}
				case ">": {
					if(fieldMeta.getNestedPath()!=null) {
						mustQuery.must(QueryBuilders.nestedQuery(fieldMeta.getNestedPath(), QueryBuilders.rangeQuery(field).gt(value), ScoreMode.Avg));
					}else {
						mustQuery.must(QueryBuilders.rangeQuery(field).gt(value));
					}
					break;
				}
				case "<": {
					if(fieldMeta.getNestedPath()!=null) {
						mustQuery.must(QueryBuilders.nestedQuery(fieldMeta.getNestedPath(), QueryBuilders.rangeQuery(field).lt(value), ScoreMode.Avg));
					}else {
						mustQuery.must(QueryBuilders.rangeQuery(field).lt(value));
					}
					break;
				}
				case ">=": {
					if(fieldMeta.getNestedPath()!=null) {
						mustQuery.must(QueryBuilders.nestedQuery(fieldMeta.getNestedPath(), QueryBuilders.rangeQuery(field).gte(value), ScoreMode.Avg));
					}else {
						mustQuery.must(QueryBuilders.rangeQuery(field).gte(value));
					}
					break;
				}
				case "<=": {
					if(fieldMeta.getNestedPath()!=null) {
						mustQuery.must(QueryBuilders.nestedQuery(fieldMeta.getNestedPath(), QueryBuilders.rangeQuery(field).lte(value), ScoreMode.Avg));
					}else {
						mustQuery.must(QueryBuilders.rangeQuery(field).lte(value));
					}
					break;
				}
				default: {
					break;
				}
				}
			}
			rootQuery.must(mustQuery);
		}

		return rootQuery;
	}
}
